package com.naiterui.ehp.bs.patient.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.naiterui.common.redis.RedisUtil;
import com.naiterui.common.repository.page.CustomizedPageConvertor;
import com.naiterui.common.repository.page.JpaPageConvertor;
import com.naiterui.common.repository.page.Page;
import com.naiterui.common.repository.page.PageParamVO;
import com.naiterui.common.repository.page.PageVO;
import com.naiterui.ehp.bp.bo.emr.PatientDiseaseBO;
import com.naiterui.ehp.bp.bo.patient.RevisitDoctorBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.constants.CommonConstant.ConsultType;
import com.naiterui.ehp.bp.constants.PaQrCodeEnum;
import com.naiterui.ehp.bp.domain.ConsultSession;
import com.naiterui.ehp.bp.domain.Department;
import com.naiterui.ehp.bp.domain.Doctor;
import com.naiterui.ehp.bp.domain.DoctorCharge;
import com.naiterui.ehp.bp.domain.DrBayConsult;
import com.naiterui.ehp.bp.domain.DrChargeConfig;
import com.naiterui.ehp.bp.domain.DrConsult;
import com.naiterui.ehp.bp.domain.DrMessageConsultOrder;
import com.naiterui.ehp.bp.domain.DrVideoConsult;
import com.naiterui.ehp.bp.domain.Hospital;
import com.naiterui.ehp.bp.domain.Label;
import com.naiterui.ehp.bp.domain.Patient;
import com.naiterui.ehp.bp.domain.PatientInquirerInfo;
import com.naiterui.ehp.bp.domain.PatientPersonDoctor;
import com.naiterui.ehp.bp.domain.UserLabel;
import com.naiterui.ehp.bp.domain.charge.DrServiceItems;
import com.naiterui.ehp.bp.support.context.ResourceHolder;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.exception.ResponseVO;
import com.naiterui.ehp.bp.support.utils.JsonMapper;
import com.naiterui.ehp.bp.utils.beans.BeanUtil;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.net.RestHttpUtil;
import com.naiterui.ehp.bs.patient.constants.CacheConstant;
import com.naiterui.ehp.bs.patient.dao.IDoctorChargeDao;
import com.naiterui.ehp.bs.patient.dao.IDoctorDao;
import com.naiterui.ehp.bs.patient.dao.IDrMessageConsultChargeDao;
import com.naiterui.ehp.bs.patient.dao.IHospitalDao;
import com.naiterui.ehp.bs.patient.dao.IPatientDao;
import com.naiterui.ehp.bs.patient.dao.IPatientPersonDoctorDao;
import com.naiterui.ehp.bs.patient.dao.mongo.ApMessageDao;
import com.naiterui.ehp.bs.patient.exception.ExceptionCodes;
import com.naiterui.ehp.bs.patient.feign.EmrFeginClient;
import com.naiterui.ehp.bs.patient.feign.VideoConsultFeignClient;
import com.naiterui.ehp.bs.patient.prodocl.Message;
import com.naiterui.ehp.bs.patient.repository.IApConsultSessionRepository;
import com.naiterui.ehp.bs.patient.repository.IApDepartmentRepository;
import com.naiterui.ehp.bs.patient.repository.IApDoctorRepository;
import com.naiterui.ehp.bs.patient.repository.IApHospitalRepository;
import com.naiterui.ehp.bs.patient.repository.IApPatientPersonDoctorRepository;
import com.naiterui.ehp.bs.patient.repository.IPatientInquirerRepository;
import com.naiterui.ehp.bs.patient.repository.IUserLabelRepository;
import com.naiterui.ehp.bs.patient.rpc.im.IConsultSessionRemoter;
import com.naiterui.ehp.bs.patient.service.ICityService;
import com.naiterui.ehp.bs.patient.service.IConsultCommentService;
import com.naiterui.ehp.bs.patient.service.IConsultSessionService;
import com.naiterui.ehp.bs.patient.service.IDoctorChargeService;
import com.naiterui.ehp.bs.patient.service.IDrServiceItemService;
import com.naiterui.ehp.bs.patient.service.IPatientDoctorService;
import com.naiterui.ehp.bs.patient.service.function.IFunctionService;
import com.naiterui.ehp.bs.patient.service.message.IMessageSendService;
import com.naiterui.ehp.bs.patient.utils.ConfigKeyUtil;
import com.naiterui.ehp.bs.patient.utils.ImgUtil;
import com.naiterui.ehp.bs.patient.utils.NoticeUtil;
import com.naiterui.ehp.bs.patient.utils.TitleUtil;
import com.naiterui.ehp.bs.patient.utils.cache.BaseParamCacheUtil;
import com.naiterui.ehp.bs.patient.utils.cache.PatientCacheUtil;
import com.naiterui.ehp.bs.patient.utils.constants.PatientConstant;
import com.naiterui.ehp.bs.patient.utils.exception.PatientBusinessException;
import com.naiterui.ehp.bs.patient.validation.PatientValidation;
import com.naiterui.ehp.bs.patient.vo.common.CityInfoVO;
import com.naiterui.ehp.bs.patient.vo.consult.ByConsultParamVO;
import com.naiterui.ehp.bs.patient.vo.doctor.ClinicDoctorVO;
import com.naiterui.ehp.bs.patient.vo.doctor.DoctorCheckVO;
import com.naiterui.ehp.bs.patient.vo.doctor.DoctorVO;
import com.naiterui.ehp.bs.patient.vo.doctor.MessageVO;
import com.naiterui.ehp.bs.patient.vo.parameters.DepartmentVO;
import com.naiterui.ehp.bs.patient.vo.patient.PatientVO;
import com.naiterui.ehp.bs.patient.vo.patient.PersonalRelation;
import com.naiterui.ehp.bs.patient.vo.remote.DoctorSearchVO;
import com.naiterui.ehp.bs.wechat.protocol.params.CodeActionInfo;
import com.naiterui.ehp.bs.wechat.protocol.result.QrCodeResult;
import com.naiterui.ehp.bs.wechat.service.IAccessTokenService;
import com.naiterui.ehp.bs.wechat.service.ICustomMsgApiService;
import com.naiterui.ehp.bs.wechat.service.IWebAuthorizeApiService;
import com.naiterui.ehp.bs.wechat.utils.WeChatApiUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 患者医生关系管理业务实现
 *
 * @version 2015年6月22日 下午12:04:01 by Amber
 * @see IPatientDoctorService
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ApPatientDoctorServiceImpl implements IPatientDoctorService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApPatientDoctorServiceImpl.class);

    private final IPatientDao patientDao;
    private final IDoctorDao doctorDao;
    private final ApMessageDao messageDao;
    private final IApDepartmentRepository departmentRepository;
    private final IDoctorChargeService doctorChargeService;
    private final IApConsultSessionRepository consultSessionRepository;
    private final IDoctorChargeDao doctorChargeDao;
    private final IHospitalDao hospitalDao;
    private final IPatientPersonDoctorDao patientPersonDoctorDao;
    private final IDrServiceItemService drServiceItemService;
    private final IDrMessageConsultChargeDao drMessageConsultChargeDao;
    private final IFunctionService functionService;
    private final IConsultSessionRemoter consultSessionRemoter;
    private final ICustomMsgApiService customMsgApiService;
    private final IWebAuthorizeApiService webAuthorizeApiServiceImpl;
    private final IMessageSendService messageSendService;
    private final IAccessTokenService accessTokenService;
    private final IApHospitalRepository hospitalRepository;
    private final ICityService cityService;
    private final IApDoctorRepository doctorRepository;
    private final IApPatientPersonDoctorRepository patientPersonDoctorRepository;
    private final IUserLabelRepository userLabelRepository;
    private final EmrFeginClient emrFeginClient;
    private final VideoConsultFeignClient videoConsultFeignClient;
    @Autowired
    private IConsultCommentService consultCommentService;
    @Autowired
    private IConsultSessionService consultSessionService;
    @Autowired
    private IPatientInquirerRepository patientInquirerRepository;

    /**
     * 根据患者id查询患者的所有私人医生列表ids
     *
     * @param patientId
     *
     * @return
     *
     * @Description IDoctorDao.getMyPrivateDoctor()
     * @Author linhf
     * @Date 2016年4月14日
     * @since 1.0.0
     */
    @Override
    public PageVO<DoctorVO> getMyPrivateDoctor(Long patientId, int page, int num, Integer accountStatus) {
        PageVO<DoctorVO> pageVo = new PageVO<>();
        // 根据条件查询医生集合
        Map<String, Object> resultMap = doctorDao.getMyPrivateDoctor(patientId, page, num, accountStatus);
        // 获取医生列表
        List<Object[]> list = (List<Object[]>) resultMap.get("list");

        Iterator it = list.iterator();// 查询医生 科室
        Iterator its = list.iterator();// 查询医生信息
        // 从缓存里获取医生科室信息
        Map<Long, String> dMap = new HashMap<>();
        List<Integer> departmentIds = new ArrayList<>();// 科室ids

        // 缓存中获取科室信息
        while (it.hasNext()) {
            Object[] obj = (Object[]) it.next();
            if (obj[4] != null && Integer.parseInt(obj[4].toString()) != 0) {// 存在科室，且非自定义
                // 缓存中获取科室信息
                DepartmentVO departmentVO = BaseParamCacheUtil.getDepartmentFromRedis(NumberUtils.toInt(obj[4].toString()));
                if (departmentVO != null) {// 缓存中存在科室
                    dMap.put(Long.parseLong(obj[4].toString()), departmentVO.getName());
                } else {
                    departmentIds
                            .add(departmentIds.size() <= 0 ? NumberUtils.toInt(obj[4].toString()) : NumberUtils.toInt(
                                    obj[4].toString()));// 缓存中未查询到的科室信息
                }
            }
        }

        // 缓存没有科室信息，数据库读取
        if (departmentIds.size() > 0) {
            List<Department> departmentList = departmentRepository.findByIdInAndAvailable(departmentIds,
                                                                                               true);
            for (Department dep : departmentList) {
                dMap.put(dep.getId().longValue(), dep.getName());// 持久层查出的科室列表：key=id，value=科室名称
            }
        }
        List<DoctorVO> resultList = new ArrayList<>();
        while (its.hasNext()) {
            Object[] obj = (Object[]) its.next();
            DoctorVO vo = new DoctorVO();
            vo.setDoctorId(Long.parseLong(obj[0].toString()));// 医生id
            vo.setName(obj[1].toString());// 医生名称
            vo.setStatus(Integer.parseInt(obj[2].toString()));// 状态
            vo.setPhoto(null != obj[3] ? ImgUtil.getFilePath(obj[3].toString()) : "");// 头像地址
            // 科室名
            if (null == obj[4] || StringUtils.isEmpty(obj[4].toString())) {
                vo.setDepartment(null == obj[5] ? "" : obj[5].toString());
            } else {
                vo.setDepartment(null == dMap.get(Long.parseLong(obj[4].toString())) ? "" : dMap.get(NumberUtils.toLong(obj[4].toString())));// 科室名
            }
            // 职称
            vo.setTitle(null != obj[6] ? TitleUtil.getTitle(NumberUtils.toInt(obj[6].toString())) : "");
            // 账户状态0:正常，1:禁用
            vo.setAccountStatus(null != obj[7] ? NumberUtils.toInt(obj[7].toString()) : null);
            resultList.add(vo);
        }

        pageVo = CustomizedPageConvertor.buildPageVOWithCustom(page, num <= 0 ? 1 : num, resultList,
                                                               NumberUtils.toInt(resultMap.get("totalCount").toString()));
        return pageVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PersonalRelation addPersonalDoctor(Long doctorId, Long patientId, Integer firstRecom, int soucre) throws BusinessException {
        Patient patient = patientDao.get(patientId);
        Doctor doctor = doctorDao.get(doctorId);
        PersonalRelation personalRelation = addPersonalDoctor(patient, doctor, soucre, false);
        // 设置首推医生
        if (firstRecom == 1) {
            patient.setRecomDoctor(doctorId);
            // 注册来源为推荐
            patient.setRegistSource(PatientConstant.REGIST_SOURCE_RECOM);
            patientDao.saveOrUpdatePatient(patient);
        }
        return personalRelation;

    }

    private PersonalRelation addPersonalDoctor(Long doctorId, Long patientId, int soucre, boolean ignoreBound) throws BusinessException {
        Patient patient = patientDao.get(patientId);
        Doctor doctor = doctorDao.get(doctorId);
        PersonalRelation personalRelation = addPersonalDoctor(patient, doctor, soucre, ignoreBound);
        return personalRelation;

    }

    /**
     * <绑定医生></p>
     * 设置医生和患者的私人医生关系
     *
     * @param patient 患者实体
     * @param doctor  医生实体
     *
     * @return
     *
     * @throws BusinessException PersonalRelation <返回值描述>
     * @Throws 异常信息
     * @History 2015年11月5日 下午11:16:24 by Amber
     */
    private PersonalRelation addPersonalDoctor(Patient patient, Doctor doctor, int source, boolean ignoreBound) throws BusinessException {

        // 患者信息校验
        // 患者不存在
        if (patient == null || patient.getId() == null) {
            LOGGER.error("addPersonalDoctor Error ; patient not exist : patientId {}, doctorId {}", null, doctor == null ? null : doctor.getId());
            throw new PatientBusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
        }
        // 患者账号被禁用
        if (patient.getAccountStatus() != 0) {
            LOGGER.error("addPersonalDoctor Error ; patient account status wrong : patientId {}, doctorId{}", patient.getId(),
                         doctor == null ? null : doctor.getId());
            throw new PatientBusinessException(ExceptionCodes.PATIENT_ACCOUNT_ERROR);
        }

        // 医生信息校验
        if (doctor == null || doctor.getId() == null) {
            LOGGER.error("addPersonalDoctor Error ; doctor not exist : patientId {}, doctorId {}", patient.getId(), null);
            throw new PatientBusinessException(ExceptionCodes.DOCTOR_NOT_EXIST);
        }
        // 医生账号被禁用
        if (doctor.getAccountStatus() != 0) {
            LOGGER.error("addPersonalDoctor Error ; doctor account status wrong : patientId {}, doctorId {}", patient.getId(), doctor.getId());
            throw new PatientBusinessException(ExceptionCodes.DOCTOR_ACCOUNT_ERROR);
        }

        Long patientId = patient.getId();
        Long doctorId = doctor.getId();

        PatientPersonDoctor patientPersonDoctor = patientPersonDoctorDao.findByPatientIdAndDoctorId(patientId, doctorId);
        if (patientPersonDoctor != null && !ignoreBound) {
            if (patientPersonDoctor.getRelationFlag() == PatientPersonDoctor.RELATION_FLAG_DELETE) {
                LOGGER.debug("医患关系已经删除 doctorId:{} patientId:{}", doctorId, patientId);
                throw new BusinessException(ExceptionCodes.DOCTOR_PATIENT_RELATION_DELETE);
            } else {
                //使用ResourceHolder回传患者id
                log.info("资源持有器线程上下文-存入数据-医患绑定异常，patientId {}", patient.getId());
                ResourceHolder.bindResource("patientId", patient.getId());
                // 已经绑定，无需再绑定
                LOGGER.error("addPersonalDoctor Error ; have bound : patientId {}, doctorId {}", patient.getId(), doctor.getId());
                throw new PatientBusinessException(ExceptionCodes.HAVE_BOUND);
            }
        }

        // 首次绑定
        if(patientPersonDoctor == null){
            // 绑定私人医生关系
            patientDao.addPersonalDoctor(doctorId, patientId, source);

            // 解除关注关系
            if (patientDao.existAttendRelation(patientId, doctor.getId())) {
                deleteAttend(patientId, doctor.getId());
            }
        }


        // 获取患者的个人医生信息
        PersonalRelation personalRelation = findPersonalDoctor(doctor, patient);
        // 非首次绑定直接返回绑定关系
        if(patientPersonDoctor != null){
            return personalRelation;
        }

        // 更新患者最新绑定医生缓存
        DoctorVO newPersonal = buildNewPersonalDoctor(doctor);

        newPersonal.setBindTime(System.currentTimeMillis());
        PatientCacheUtil.addPatientNewPersonalDoctorToRedis(patientId, newPersonal);

        // 优先级低，避免影响原有妥投回调逻辑 by mudou
        /*try {
            this.scaleService.saveMouthTest(doctorId, patientId, patient.getOpenId());
            this.scaleService.publishEdu(doctor, patientId);
        } catch (Exception e) {
            LOGGER.error("[患者扫码量表信息存储]saveScaleRemind ERROR ", e);
        }*/
        //绑定私人医生后，设置医生图文咨询费
        //this.remoter.addDoctorDefaultMessageCharge(patientId, doctorId);
        // 异步处理通知
        NoticeUtil.newPatientNotice(doctorId, patientId);
        return personalRelation;
    }

    @Override
    public PersonalRelation findPersonalDoctorByIds(Long doctorId, Long patientId) throws BusinessException {
        Patient patient = patientDao.get(patientId);
        Doctor doctor = doctorDao.get(doctorId);
        return findPersonalDoctor(doctor, patient);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DoctorVO addAttendDoctor(Long patientId, Long doctorId) throws BusinessException {
        Patient patient = patientDao.get(patientId);
        // 患者不存在
        if (patient == null) {
            LOGGER.error("addAttendDoctor Error ; patient not exist : patientId {}, doctorId {}", patientId, doctorId);
            throw new PatientBusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
        }
        // 患者账号被禁用
        if (patient.getAccountStatus() != 0) {
            LOGGER.error("addAttendDoctor Error ; patient account status wrong : patientId {}, doctorId {}", patientId, doctorId);
            throw new PatientBusinessException(ExceptionCodes.PATIENT_ACCOUNT_ERROR);
        }

        Doctor doctor = doctorDao.get(doctorId);
        // TODO:密钥校验

        if (doctor == null || doctor.getAccountStatus() != 0) {
            LOGGER.error("addAttendDoctor Error ; doctor account status wrong : patientId {}, doctorId {}", patientId, doctorId);
            throw new PatientBusinessException(ExceptionCodes.DOCTOR_NOT_EXIST);
        }
        // 已经关注无需再关注
        if (patientDao.existAttendRelation(patientId, doctorId)) {
            LOGGER.error("addAttendDoctor Error ; doctor account status wrong : patientId {}, doctorId {}", patientId, doctorId);
            throw new PatientBusinessException(ExceptionCodes.HAVE_ATTENDED);
        }

        // 关注医生
        patientDao.attendDoctor(doctorId, patientId);

        DoctorVO doctorVo = new DoctorVO();
        doctorVo.setRelation(PatientConstant.RELATIONN_TYPE_ATTEND);
        return doctorVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DoctorVO deleteAttend(Long patientId, Long doctorId) throws BusinessException {
        Patient patient = patientDao.get(patientId);
        // 患者不存在
        if (patient == null) {
            LOGGER.error("deleteAttend Error ; patient not exist : patientId {}, doctorId {}", patientId, doctorId);
            throw new PatientBusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
        }
        // 患者账号被禁用
        if (patient.getAccountStatus() != 0) {
            LOGGER.error("deleteAttend Error ; patient account status wrong : patientId {}, doctorId {}", patientId, doctorId);
            throw new PatientBusinessException(ExceptionCodes.PATIENT_ACCOUNT_ERROR);
        }
        // 不存在关注关系，不需要解除关注
        if (!patientDao.existAttendRelation(patientId, doctorId)) {
            LOGGER.error("deleteAttend Error ; no attention : patientId {}, doctorId {}", patientId, doctorId);
            throw new PatientBusinessException(ExceptionCodes.HAVE_NO_ATTENTION);
        }

        // 删除关联关系
        patientDao.cancelAttend(patientId, doctorId);
        DoctorVO doctorVo = new DoctorVO();
        doctorVo.setRelation(PatientConstant.RELATIONN_TYPE_NULL);
        return doctorVo;
    }

    @Override
    public PageVO<DoctorVO> getMyDoctors(Long patientId, Pageable pageable, String scene) throws BusinessException {
        Patient patient = patientDao.get(patientId);

        PatientValidation.accountStatusValidate(patient, String.format("getMyDoctors Error ; patient not exist : patientId %s", patientId));

        functionService.isAllDoctorsDisable(patientId);

        // 扫码绑定私人医生
        this.sceneHandle(patientId, scene);

        PageVO<DoctorVO> doctorPage = doctorDao.getMyDoctors(patientId, pageable);
        List<DoctorVO> results = doctorPage.getResult();
        // 获取医生信息
        getDoctorDetailInfo(patientId, results, null);
        return doctorPage;
    }

    @Override
    public PageVO<DoctorVO> getClinicDoctors(Integer departmentId, Integer cityId, Long patientId, Long labelId,
        Pageable pageable, String keyword) throws BusinessException {

        if (patientId != null) {
            Patient patient = patientDao.get(patientId);
            // 患者不存在
            if (patient == null) {
                LOGGER.error("getDoctors Error ; patient not exist : patientId {}, departmentId {}, cityId {}", patientId,
                             departmentId, cityId);
                throw new PatientBusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
            }
            // 患者账号被禁用
            if (patient.getAccountStatus() != 0) {
                LOGGER.error("getDoctors Error ; patient account status wrong : patientId {}, departmentId {}, cityId {}", patientId, departmentId,
                             cityId);
                throw new PatientBusinessException(ExceptionCodes.PATIENT_ACCOUNT_ERROR);
            }
        }
        // 获取科室ID列表
        List<Integer> departmentIds = new ArrayList<>();
        if (null != departmentId) {
            departmentIds = departmentRepository.findByParentIdAndAvailable(departmentId, true);
            departmentIds.add(departmentId);
        }
        log.info("科室列表信息======     departmentIds={}",departmentIds);
        // 获取城市ID列表
        List<Integer> cityIds = new ArrayList<>();
        if (null != cityId) {
            List<CityInfoVO> cityInfoVOS = cityService.getCityList(cityId);
            cityIds = cityInfoVOS.stream().map(city -> city.getId()).collect(Collectors.toList());
            cityIds.add(cityId);
        }

        // 翻页数据
        PageVO<DoctorVO> doctorPage = null;

        log.info("参数  ========  keyword",keyword);
        if (StringUtils.isNoneBlank(keyword)) {
            // 医生姓名或医院名称搜索
            List<Hospital> hospitalList = hospitalRepository.findByNameLike(keyword);
            List<Integer> hospitalIds = new ArrayList<>();
            hospitalIds = hospitalList.stream().map(e -> e.getId()).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(hospitalIds)) {
                if(1==labelId){
                    doctorPage = JpaPageConvertor
                            .pageConvert(doctorRepository.getClickDoctorByDoctorNameOrHospital("%" + keyword + "%", hospitalIds,labelId, pageable));
                }else{
                    doctorPage = JpaPageConvertor
                            .pageConvert(doctorRepository.getClickDoctorByDoctorNameOrHospitalAll("%" + keyword + "%", hospitalIds, pageable));
                }
            } else {
                if(1==labelId){
                    doctorPage = JpaPageConvertor
                            .pageConvert(doctorRepository.getClickDoctorByDoctorName("%" + keyword + "%",labelId, pageable));
                }else{
                    doctorPage = JpaPageConvertor
                            .pageConvert(doctorRepository.getClickDoctorByDoctorNameAll("%" + keyword + "%", pageable));
                }

            }

        } else if (departmentIds.size() > 0 || cityIds.size() > 0) {
            log.info("参数  ========  departmentIds={},cityIds={}",departmentIds.size(),cityIds.size());
            if (departmentIds.size() > 0 && cityIds.size() > 0) {
                if(1==labelId){
                    doctorPage = JpaPageConvertor.pageConvert(
                            doctorRepository.getClickDoctorByDepartmentAndCity(departmentIds, cityIds, labelId, pageable));
                }else{
                    doctorPage = JpaPageConvertor.pageConvert(
                            doctorRepository.getClickDoctorByDepartmentAndCityAll(departmentIds, cityIds, pageable));
                }

            } else if (departmentIds.size() > 0) {
                if(1==labelId){
                    doctorPage = JpaPageConvertor
                            .pageConvert(doctorRepository.getClickDoctorByDepartment(departmentIds, labelId, pageable));
                    log.info("根据科室信息获取医生信息  ========  doctorPage={},departmentIds={},labelId={}",doctorPage,departmentIds,labelId);
                }else{
                    doctorPage = JpaPageConvertor
                            .pageConvert(doctorRepository.getClickDoctorByDepartmentAll(departmentIds, pageable));
                    log.info("根据科室信息获取医生信息  ========  doctorPage={},departmentIds={},labelId={}",doctorPage,departmentIds,labelId);
                }

            } else if (cityIds.size() > 0) {
                if(1==labelId){
                    doctorPage =
                            JpaPageConvertor.pageConvert(doctorRepository.getClickDoctorByCity(cityIds, labelId, pageable));
                }else{
                    doctorPage =
                            JpaPageConvertor.pageConvert(doctorRepository.getClickDoctorByCityAll(cityIds, pageable));
                }

            }
        } else {
            if(1==labelId){
                doctorPage = JpaPageConvertor.pageConvert(doctorRepository.getClickDoctor(labelId, pageable));
            }else{
                doctorPage = JpaPageConvertor.pageConvert(doctorRepository.getClickDoctorAll(pageable));
            }

        }

        // 处理relation数据
        List<DoctorVO> list = doctorPage.getResult();
        List<Long> doctorIds = list.stream().map(dr -> dr.getDoctorId()).collect(Collectors.toList());
        List<PatientPersonDoctor> patientPersonDoctors = patientPersonDoctorRepository.findByPersonPKPatientIdAndPersonPKDoctorIdIn(patientId,
                                                                                                                                         doctorIds);
        Map<Long, PatientPersonDoctor> patientPersonDoctorMap = new HashMap<>();
        for (PatientPersonDoctor personal : patientPersonDoctors) {
            patientPersonDoctorMap.put(personal.getPersonPK().getDoctorId(), personal);
        }

        list.forEach(doctor -> doctor.setRelation(patientPersonDoctorMap.get(doctor.getDoctorId()) != null ?
                                                          DoctorVO.DOCTOR_PATIENT_RELATION_PERSONAL : DoctorVO.DOCTOR_PATIENT_RELATION_NO));

        // PageVO<DoctorVO> doctorPage = doctorDao.getAllDoctors(pageable);
        // List<DoctorVO> results = doctorPage.getResult();
        // 获取医生信息
        getDoctorDetailInfo(null, list, null);
        return doctorPage;
    }

    @Override
    public PageVO<DoctorVO> getClinicDoctorList(Integer departmentId, Integer cityId, Long patientId, Long labelId,
        Pageable pageable, String keyword) throws BusinessException {
        if (patientId != null) {
            Patient patient = patientDao.get(patientId);
            // 患者不存在
            if (patient == null) {
                LOGGER.error("getDoctors Error ; patient not exist : patientId {}, departmentId {}, cityId {}",
                    patientId, departmentId, cityId);
                throw new PatientBusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
            }
            // 患者账号被禁用
            if (patient.getAccountStatus() != 0) {
                LOGGER.error(
                    "getDoctors Error ; patient account status wrong : patientId {}, departmentId {}, cityId {}",
                    patientId, departmentId, cityId);
                throw new PatientBusinessException(ExceptionCodes.PATIENT_ACCOUNT_ERROR);
            }
        }
        // 获取科室ID列表
        List<Integer> departmentIds = new ArrayList<>();
        if (null != departmentId) {
            departmentIds = departmentRepository.findByParentIdAndAvailable(departmentId, true);
            departmentIds.add(departmentId);
        }
        log.info("科室列表信息======     departmentIds={}", departmentIds);
        // 获取城市ID列表
        List<Integer> cityIds = new ArrayList<>();
        if (null != cityId) {
            List<CityInfoVO> cityInfoVOS = cityService.getCityList(cityId);
            cityIds = cityInfoVOS.stream().map(city -> city.getId()).collect(Collectors.toList());
            cityIds.add(cityId);
        }

        List<Integer> finalDepartmentIds = departmentIds;
        List<Integer> finalCityIds = cityIds;
        Specification<Doctor> spec = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            if (UserLabel.LABEL_ID_TYPE_CLINIC == labelId.intValue()) {
                // 连表查询
                Join<Doctor, UserLabel> userLabel = root.join("userLabel", JoinType.INNER);
                predicate.getExpressions().add(cb.equal(userLabel.get("userType"), UserLabel.USER_TYPE_DOCTOR));
                predicate.getExpressions().add(cb.equal(userLabel.get("labelId"), labelId));
            }
            predicate.getExpressions().add(cb.equal(root.get("status"), Doctor.AUTH_STATUS_SUCCESS));
            predicate.getExpressions().add(cb.equal(root.get("recordStatus"), Doctor.RECORD_STATUS_SUCCESS));
            if (StringUtils.isNoneBlank(keyword)) {
                // 医生姓名或医院名称搜索
                List<Hospital> hospitalList = hospitalRepository.findByNameLike(keyword);
                List<Integer> hospitalIds = hospitalList.stream().map(e -> e.getId()).collect(Collectors.toList());
                Predicate name = cb.like(root.get("name"), "%" + keyword + "%");
                if (CollUtil.isNotEmpty(hospitalIds)) {
                    CriteriaBuilder.In<Integer> hospitalId = cb.in(root.get("hospitalId"));
                    for (Integer id : hospitalIds) {
                        hospitalId.value(id);
                    }
                    predicate.getExpressions().add(cb.or(name, hospitalId));
                } else {
                    predicate.getExpressions().add(name);
                }
            }
            if (CollUtil.isNotEmpty(finalDepartmentIds)) {
                CriteriaBuilder.In<Integer> deptId = cb.in(root.get("departmentId"));
                for (Integer id : finalDepartmentIds) {
                    deptId.value(id);
                }
                predicate.getExpressions().add(deptId);
            }
            if (CollUtil.isNotEmpty(finalCityIds)) {
                CriteriaBuilder.In<Integer> cityId1 = cb.in(root.get("cityId"));
                for (Integer id : finalCityIds) {
                    cityId1.value(id);
                }
                predicate.getExpressions().add(cityId1);
            }
            return predicate;
        };

        PageVO<Doctor> page = JpaPageConvertor.pageConvert(doctorRepository.findAll(spec, pageable));
        List<DoctorVO> list = page.getResult().stream().map(d -> {
            DoctorVO vo = new DoctorVO();
            BeanUtil.copyProperties(d, vo);
            vo.setClinicFlag(true);
            vo.setPhoto(d.getHeadUrl());
            vo.setDoctorId(d.getId());
            vo.setAccountStatus(d.getAccountStatus());
            return vo;
        }).collect(Collectors.toList());
        // 处理relation数据
        List<PatientPersonDoctor> patientPersonDoctors =
            patientPersonDoctorRepository.findByPersonPKPatientIdAndPersonPKDoctorIdIn(patientId,
                list.stream().map(DoctorVO::getDoctorId).collect(Collectors.toList()));
        Map<Long, PatientPersonDoctor> patientPersonDoctorMap = new HashMap<>();
        for (PatientPersonDoctor personal : patientPersonDoctors) {
            patientPersonDoctorMap.put(personal.getPersonPK().getDoctorId(), personal);
        }

        list.forEach(doctor -> doctor.setRelation(patientPersonDoctorMap.get(doctor.getDoctorId()) != null
            ? DoctorVO.DOCTOR_PATIENT_RELATION_PERSONAL : DoctorVO.DOCTOR_PATIENT_RELATION_NO));

        // 获取医生信息
        getDoctorDetailInfo(null, list, null);
        return PageVO.newBuilder().pageNo(page.getPageNo()).pageSize(page.getPageSize())
            .totalCount(page.getTotalCount()).result(list).build();
    }

    /**
     * @param patientId
     * @param results
     *
     * @throws BusinessException
     * @Title: getDoctorDetailInfo
     * @author: chengqiang
     * @Description: 设置医生详细信息
     * @date: 2018年8月8日 下午5:33:09
     * @return: void
     */
    private void getDoctorDetailInfo(Long patientId, List<DoctorVO> results, Long doctorId) throws BusinessException {
        if (CollectionUtils.isEmpty(results)) {
            return;
        }
        List<Long> doctorIdList = new ArrayList<>();
        Set<Integer> hospitalIdSet = new HashSet<>();
        for (DoctorVO vo : results) {
            doctorIdList.add(vo.getDoctorId());
            hospitalIdSet.add(vo.getHospitalId());
            /*// 设置选中医生
            if (null != doctorId && vo.getDoctorId().equals(doctorId)) {
                vo.setChecked(true);
            }*/
        }

        // 获取医生开通服务集合 key:医生id value：服务信息
        Map<Long, DrServiceItems> drServiceItemsMap = drServiceItemService.getDoctorChargeItemsMap(doctorIdList);
        // 医生收费信息集合 key:医生id；value收费信息
        Map<Long, DoctorCharge> drChargeMap = doctorChargeDao.getChargeMap(doctorIdList, DrChargeConfig.TYPE_VIDEO);
        // 相关医院集合 key：医院id；value：医院
        Map<Integer, Hospital> hospitalMap = hospitalDao.getHospitalMap(hospitalIdSet);

        // 图文咨询集合key：医生id value：收费金额
        Map<Long, Integer> consultCostMap = new HashMap<>();
        // 图文咨询付费状态集合 key：医生id value：是否付费
        Map<Long, Boolean> consultPayStatusMap = new HashMap<>();
//        if (doctorIdList.size() > 0 && patientId != null) {
        if (doctorIdList.size() > 0) {
            consultCostMap = drMessageConsultChargeDao.getChargeListByPatientId(patientId, doctorIdList);
            Long[] dids = new Long[doctorIdList.size()];
            consultPayStatusMap = consultSessionRemoter.aliveMap(patientId, doctorIdList.toArray(dids));
            // this.drMessageConsultOrderService.findPayStatusByPatientAndDoctors(patientId,
            // doctorIdList);
        }

        //获取医生好评率
        Map<Long, String> praisRateMap = consultCommentService.getPraiseRate(doctorIdList);

        //获取医生患者数
        Map<Long, Integer> doctorPatientsMap = doctorDao.getDoctorPatientNumByIds(doctorIdList);

        //获取医生服务患者数
        //Map<Long, Integer> doctorServiceMap = consultSessionService.getDoctorServiceNum(doctorIdList);

        // 医生科室名处理
        for (DoctorVO vo : results) {
            // 科室
            if (vo.getDepartmentId() != null && vo.getDepartmentId() != 0) {// 存在科室，且非自定义
                // 医生有科室，先从缓存查询科室信息
                DepartmentVO departmentVO = BaseParamCacheUtil.getDepartmentFromRedis(vo.getDepartmentId());
                if (departmentVO != null) {
                    // 设置患者科室
                    vo.setDepartment(departmentVO.getName());
                } else {
                    // 缓存中没有则去读库查询
                    Department department = departmentRepository.get(vo.getDepartmentId());
                    if (department != null) {
                        // 设置患者科室
                        vo.setDepartment(department.getName());
                    }
                }
            } else {
                vo.setDepartment(vo.getDepartment() == null ? "" : vo.getDepartment());
            }

            // 医院信息设置
            Hospital hospital = hospitalMap.get(vo.getHospitalId());
            if (hospital != null) {
                vo.setHospital(hospital.getName());
            }

            // 图文咨询费用，新版不再用屏蔽by xuchong
            // setDoctorChargeInfo(vo);

            // 设置患者发送的最后一条消息
            vo.setMessage(getFirstMesageByPD(patientId, vo.getDoctorId()));

            // 设置当前患者未支付账单数 新版不在用屏蔽by xuchong
            // vo.setNoPayNum(unPayNum);

            Integer consultCost = consultCostMap.get(vo.getDoctorId()) != null ? consultCostMap.get(vo.getDoctorId()) : 0;
            // 会话是否收费
            vo.setConsultPayType(consultCost > 0 ? CommonConstant.CONSULT_PAYTYPE_CHARGE : CommonConstant.CONSULT_PAYTYPE_FREE);
            // 图文咨询费用
            vo.setConsultCost(consultCost);
            // 图文支付状态
            boolean sessionAlive = consultPayStatusMap.get(vo.getDoctorId()) == null ? false : consultPayStatusMap.get(vo.getDoctorId());
            vo.setPayStatus(sessionAlive ? DrMessageConsultOrder.MESSAGE_PAY_STATUS_YES : DrMessageConsultOrder.MESSAGE_PAY_STATUS_NO);
            // 在线状态，默认都为在线状态 V1.4 @2015-11-16 by zhoupeipei
            vo.setStatus(1);

            if (drChargeMap != null && drChargeMap.get(vo.getDoctorId()) != null) {
                // 获取医生服务信息
                DoctorCharge doctorCharge = drChargeMap.get(vo.getDoctorId());
                Integer videoCost = (doctorCharge.getCharge() != null) ? doctorCharge.getCharge() : Integer.valueOf(0);
                Long chargePeriod = (doctorCharge.getChargePeriod() != null) ? doctorCharge.getChargePeriod() : Long.valueOf(0);
                // 转化成分钟
                Integer videoLength = chargePeriod.intValue() / (1000 * 60);
                // 设置视频咨询费用
                vo.setVideoCost(videoCost);
                // 设置视频咨询收费时长
                vo.setVideoLength(videoLength);
                // 已开通视频咨询
                vo.setIsOpenVideo(1);
            } else {
                vo.setVideoCost(0);
                // 未开通视频咨询
                vo.setIsOpenVideo(0);
            }

            // 患者微信是否咨询中医生
            if (patientId != null) {
                String aimDoctor = RedisUtil.hashOps().hget(CommonConstant.CONSULT_PATIENT_AIM, patientId.toString());
                if (StringUtils.isNumeric(aimDoctor) && Long.valueOf(aimDoctor).equals(vo.getDoctorId())) {
                    vo.setChatting(true);
                }

                // 医生患者是否存在进行中会话
                vo.setDuring(RedisUtil.keyOps().existsKey(CacheConstant.getSessionKey(vo.getDoctorId(), patientId)));
            }

            //设置好评率
            vo.setPraiseRate(praisRateMap.get(vo.getDoctorId()));
            //设置医生服务患者数
            vo.setPatientNum(doctorPatientsMap.get(vo.getDoctorId()));
            //医生服务次数（会话）
            Integer videoConsultCount = Optional.ofNullable(videoConsultFeignClient.getVideoConsultCountByStatus(vo.getDoctorId(), DrVideoConsult.VIDEO_CONSULT_STATUS_ALL)).orElse(0);
            //Integer textConsultCount = null != doctorServiceMap.get(vo.getDoctorId()) ? doctorServiceMap.get(vo.getDoctorId()) : 0;
            long textConsultCount = consultSessionRepository.countByDoctorIdAndEndTimeIsNotNull(vo.getDoctorId());
            Integer allCount = videoConsultCount + (int) textConsultCount;
            //设置医生服务数（会话数）
            vo.setServiceNum(String.valueOf(allCount));
        }
    }

    @Override
    public DoctorVO getDoctorDetail(Long doctorId, Long patientId) throws BusinessException {
        // 判断医患关系是否删除
        functionService.isdDleteDoctorPatientRelation(doctorId, patientId);

        return findDoctorDetail(doctorId, patientId);
    }

    @Override
    public DoctorVO getClinicDetail(Long doctorId, Long patientId) throws BusinessException {
        return findDoctorDetail(doctorId, patientId);
    }

    @Override
    public ClinicDoctorVO getClinicDoctorDetail(Long patientId, Long doctorId) throws BusinessException {
        // 密钥验证
        Doctor doctor = doctorDao.get(doctorId);
        // 医生不存在
        if (doctor == null) {
            LOGGER.error("getDoctorDetail Error ; doctor not exist :  doctorId {}", doctorId);
            throw new PatientBusinessException(ExceptionCodes.DOCTOR_NOT_EXIST);
        }

        // 医生账号异常
        if (doctor.getAccountStatus() != 0) {
            LOGGER.error("getDoctorDetail Error ; doctor account status wrong : doctorId {}", doctorId);
            throw new PatientBusinessException(ExceptionCodes.DOCTOR_ACCOUNT_DISABLE);
        }

        // 看全部医生是否禁用

        ClinicDoctorVO doctorVO = new ClinicDoctorVO();

        // 获取患者未支付账单数 新版本不在使用，屏蔽by xuchong
        // int unPayNum = consultBillService.getPatientUnPayBillNum(patientId);

        // 基础信息
        doctorVO.setDoctorId(doctor.getId());
        doctorVO.setName(doctor.getName() == null ? "" : doctor.getName());
        doctorVO.setAuthStatus(doctor.getStatus());
        doctorVO.setPhotoOnState(doctor.getHeadUrl(), doctor.getStatus());
        doctorVO.setTitle(doctor.getTitleId() == null ? "" : TitleUtil.getTitle(doctor.getTitleId()));
        doctorVO.setExpertise(doctor.getExpertise() == null ? "" : doctor.getExpertise());
        doctorVO.setIntroduction(doctor.getIntroduction() == null ? "" : doctor.getIntroduction());

        // 科室
        if (doctor.getDepartmentId() != null && doctor.getDepartmentId() != 0) {// 存在科室，且非自定义
            // 医生有科室，先从缓存查询科室信息
            DepartmentVO departmentVO = BaseParamCacheUtil.getDepartmentFromRedis(doctor.getDepartmentId());
            if (departmentVO != null) {
                // 设置患者科室
                doctorVO.setDepartment(departmentVO.getName());
            } else {
                // 缓存中没有则去读库查询
                Department department = departmentRepository.get(doctor.getDepartmentId());
                if (department != null) {
                    // 设置患者科室
                    doctorVO.setDepartment(department.getName());
                }
            }
        } else {
            doctorVO.setDepartment(doctor.getCustomDepartment() == null ? "" : doctor.getCustomDepartment());
        }

        // 医院名称
        String hpName = doctorDao.getHospitalName(doctor.getHospitalId());
        if (StringUtils.isEmpty(hpName)) {
            doctorVO.setHospital(doctor.getHospitalName() == null ? "" : doctor.getHospitalName());
        } else {
            doctorVO.setHospital(hpName);
        }
        String ptSpreadCodeKey = CommonConstant.REDIS_PRE_DR + "pt_code_" + doctorId;
        String cachePtUrl = (String) RedisUtil.valueOps().getObject(ptSpreadCodeKey);
        if (cachePtUrl == null) {
            LOGGER.info("从患者中将患者二维码返回" + cachePtUrl);
            QrCodeResult result = WeChatApiUtil.getQrCodeStr(accessTokenService.accessToken(), PaQrCodeEnum.INVITE.getActionName(),
                                                             new CodeActionInfo.Scene(PaQrCodeEnum.INVITE
                                                                                              .getValue() + doctorId));
            if (result != null && result.getUrl() != null) {
                RedisUtil.valueOps().setObject(ptSpreadCodeKey, result.getUrl(), 24 * 60 * 60);
            }
            cachePtUrl = null != result ? StringUtils.trimToEmpty(result.getUrl()) : "";
        }
        doctorVO.setQrCode(cachePtUrl);

        // 处理relation数据
        List<Long> doctorIds = Arrays.asList(new Long[]{doctorId});
        List<PatientPersonDoctor> patientPersonDoctors = patientPersonDoctorRepository.findByPersonPKPatientIdAndPersonPKDoctorIdIn(patientId,
                                                                                                                                         doctorIds);
        Map<Long, PatientPersonDoctor> patientPersonDoctorMap = new HashMap<>();
        for (PatientPersonDoctor personal : patientPersonDoctors) {
            patientPersonDoctorMap.put(personal.getPersonPK().getDoctorId(), personal);
        }
        doctorVO.setRelation(patientPersonDoctorMap.get(doctorId) != null ?
                                     DoctorVO.DOCTOR_PATIENT_RELATION_PERSONAL : DoctorVO.DOCTOR_PATIENT_RELATION_NO);

        return doctorVO;
    }

    @Override
    public PageVO<DoctorVO> findDoctors(String keyword, Integer departmentId, Integer cityId, Long patientId, int page, int num)
            throws BusinessException {
        if (patientId != null) {
            Patient patient = patientDao.get(patientId);
            // 患者不存在
            if (patient == null) {
                LOGGER.error("findDoctors Error ; patient not exist: patientId {}, keyword {}, departmentId {}, cityId {}", patientId, keyword,
                             departmentId, cityId);
                throw new PatientBusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
            }
            // 患者账号被禁用
            if (patient.getAccountStatus() != 0) {
                LOGGER.error("findDoctors Error ; patient account status wrong : patientId {}, keyword {}, departmentId {}, cityId {}", patientId,
                             keyword, departmentId, cityId);
                throw new PatientBusinessException(ExceptionCodes.PATIENT_ACCOUNT_ERROR);
            }
        }
        PageVO<DoctorVO> pageVo = new PageVO<>();
        Page<Object[]> results = doctorDao.findDoctors(keyword, departmentId, cityId, page, num);
        List<DoctorVO> voResults = new ArrayList<>();
        pageVo = CustomizedPageConvertor.buildPageVOWithCustom(results.getPageNo(), results.getPageSize(), voResults, (int) results.getTotalCount());
        for (Object[] objects : results.getResult()) {
            DoctorVO vo = new DoctorVO();
            Long doctorId = ((BigInteger) objects[0]).longValue();
            vo.setDoctorId(doctorId);
            vo.setName(objects[1] == null ? "" : objects[1].toString());
            // 查询医生限制为：已认证医生.头像正常设置.
            vo.setPhoto(ImgUtil.getDoctorPhoto(objects[2] == null ? "" : objects[2].toString()));
            vo.setTitle(objects[3] == null ? "" : TitleUtil.getTitle(Integer.valueOf(objects[3].toString())));
            // 与当前用户关系
            boolean personal = patientDao.existPersonalRelation(patientId, doctorId);
            boolean attend = patientDao.existAttendRelation(patientId, doctorId);
            if (personal) {
                vo.setRelation(PatientConstant.RELATIONN_TYPE_PERSONAL);
            } else if (attend) {
                vo.setRelation(PatientConstant.RELATIONN_TYPE_ATTEND);
            } else {
                vo.setRelation(PatientConstant.RELATIONN_TYPE_NULL);
            }

            vo.setPraiseNum(objects[8] == null ? 0 : Integer.parseInt(objects[8].toString()));
            // 科室
            if (objects[4] != null && (Integer) objects[4] != 0) {// 存在科室，且非自定义
                // 医生有科室，先从缓存查询科室信息
                DepartmentVO departmentVO = BaseParamCacheUtil.getDepartmentFromRedis((Integer) objects[4]);
                if (departmentVO != null) {
                    // 设置患者科室
                    vo.setDepartment(departmentVO.getName());
                } else {
                    // 缓存中没有则去读库查询
                    Department department = departmentRepository.get((Integer) objects[4]);
                    if (department != null) {
                        // 设置患者科室
                        vo.setDepartment(department.getName());
                    }
                }
            } else {
                vo.setDepartment(objects[10].toString() == null ? "" : objects[10].toString());
            }

            // 医院名称
            Object hid = objects[5];
            if (hid == null || (Integer) hid == 0) {
                vo.setHospital(objects[6] == null ? "" : objects[6].toString());
            } else {
                String hpName = doctorDao.getHospitalName(Integer.valueOf(objects[5].toString()));
                vo.setHospital(hpName == null ? "" : hpName);
            }

            // 在线状态，默认都为在线状态 V1.4 @2015-11-16 by zhoupeipei
            vo.setStatus(1);
            // if (validateDoctorOnline(doctorId)) {
            // vo.setStatus(1);
            // } else {
            // vo.setStatus(0);
            // }
            // 医生认证状态
            vo.setAuthStatus(objects[9] == null ? null : Integer.valueOf(objects[9].toString()));
            vo.setExpertise(objects[7] == null ? "" : objects[7].toString());
            voResults.add(vo);
        }
        return pageVo;
    }

    @Override
    public DoctorVO getSimpleDoctor(Long doctorId) throws Exception {
        Doctor doctor = doctorDao.get(doctorId);
        if (doctor == null) {
            LOGGER.error("getSimpleDoctor Error ; doctor account status wrong : doctorId {}", doctorId);
            throw new PatientBusinessException(ExceptionCodes.DOCTOR_NOT_EXIST);
        }
        DoctorVO simple = new DoctorVO();
        simple.setDoctorId(doctor.getId());
        simple.setName(doctor.getName() == null ? "" : doctor.getName());
        simple.setPhotoOnState(doctor.getHeadUrl(), doctor.getStatus());
        simple.setAuthStatus(doctor.getStatus());
        simple.setTitle(doctor.getTitleId() == null ? "" : TitleUtil.getTitle(doctor.getTitleId()));
        if ((doctor.getHospitalId() != null) && (0 != doctor.getHospitalId())) {
            simple.setHospital(doctorDao.getHospitalName(doctor.getHospitalId()));
        } else {
            simple.setHospital(doctor.getHospitalName());
        }
        // 科室
        if (doctor.getDepartmentId() != null && doctor.getDepartmentId() != 0) {// 存在科室，且非自定义
            // 医生有科室，先从缓存查询科室信息
            DepartmentVO departmentVO = BaseParamCacheUtil.getDepartmentFromRedis(doctor.getDepartmentId());
            if (departmentVO != null) {
                // 设置患者科室
                simple.setDepartment(departmentVO.getName());
            } else {
                // 缓存中没有则去读库查询
                Department department = departmentRepository.get(doctor.getDepartmentId());
                if (department != null) {
                    // 设置患者科室
                    simple.setDepartment(department.getName());
                }
            }
        } else {
            simple.setDepartment(doctor.getCustomDepartment() == null ? "" : doctor.getCustomDepartment());
        }

        return simple;
    }

    /**
     * 获取医生粉丝数
     *
     * @param doctorId
     *
     * @return
     *
     * @throws Exception DoctorVo <返回值描述>
     * @History 2015年6月26日 下午7:54:50 by Amber
     */
    @Override
    public int getDoctorFansNum(Long doctorId) throws Exception {
        return doctorDao.getFansNum(doctorId);
    }

    /**
     * 获取医生门诊时间
     *
     * @param doctorId 医生id
     *
     * @return String <返回值描述>
     *
     * @History 2015年6月28日 下午9:51:24 by Amber
     */
    private List<String> getDoctorConsults(Long doctorId) {
        List<String> consults = new ArrayList<>();
        if (doctorId == null || doctorId < 0) {
            return consults;
        }

        List<DrConsult> consultList = doctorDao.getDoctorConsults(doctorId);
        for (DrConsult consult : consultList) {
            // 周几
            String weekNum = PatientConstant.WEEKS.get(consult.getWeekIndex());
            boolean have = false;
            // 上午
            if (consult.getAm() == 1) {
                weekNum += " " + PatientConstant.AM;
                have = true;
            }
            // 下午
            if (consult.getPm() == 1) {
                weekNum += " " + PatientConstant.PM;
                have = true;
            }
            // 晚上
            if (consult.getNight() == 1) {
                weekNum += " " + PatientConstant.NIGHT;
                have = true;
            }
            if (have) {
                consults.add(weekNum);
            }
        }
        return consults;
    }

    /**
     * 获取医生在线状态
     *
     * @param doctorId 医生id
     *
     * @return boolean <返回值描述>
     *
     * @History 2015年6月29日 下午3:45:26 by Amber
     */
    @Override
    public boolean validateDoctorOnline(Long doctorId) {
        try {
            // 读取医生互联网医院在线问诊时间
            List<DrBayConsult> bayConsults = doctorDao.getDoctorBayConsults(doctorId);
            // 获取当天为周几
            int todayWeek = DateUtil.getWeekNum(new Date());
            // 查找医生周todayWeek的出诊时间设置信息
            for (DrBayConsult bayConsult : bayConsults) {
                if (bayConsult.getWeekIndex() == todayWeek) {// 匹配当天问诊时间
                    // 获取当天线上问诊时间字符串，“;”分隔
                    String times = bayConsult.getWork();
                    // 未编辑当天问诊时间，返回不在线。
                    if (StringUtils.isEmpty(times)) {
                        return false;
                    }

                    String[] timeList = times.split(";");
                    // 查看当前时间是否在线
                    for (String tmp : timeList) {
                        // 问诊时间段 例：8:00-15:00
                        // 当前时间分值 例:12:00
                        String now = DateUtil.formatDate(new Date(), DateUtil.TIME_MIN_FORMAT);
                        // 问诊时间段开始时间,例:8:00
                        String start = tmp.substring(0, tmp.indexOf("-"));
                        // 问诊时间段结束时,例:15:00
                        String end = tmp.substring(tmp.indexOf("-") + 1);
                        // 当前时间大于等于问诊时间段开始时间&&当前时间小于等于问诊时间段结束时间则为在线状态
                        if ((start.equals(now) || DateUtil.checkDayBeforeDayByStr(start, now, DateUtil.TIME_MIN_FORMAT))
                                && (now.equals(end) || DateUtil.checkDayBeforeDayByStr(now, end, DateUtil.TIME_MIN_FORMAT))) {
                            return true;
                        }
                    }

                }
            }
        } catch (Exception e) {
            LOGGER.error("validateDoctorOnline ERROR ;", e);
        }

        return false;
    }

    @Override
    public String getDoctorName(Long doctorId) {
        if (doctorId == null) {
            return "";
        }
        Doctor doctor = doctorDao.get(doctorId);

        return doctor == null ? "" : doctor.getName();
    }

    @Override
    public PageVO<DoctorVO> getDoctors(String keyword, Integer departmentId, Integer cityId, Long patientId, int page, int num)
            throws BusinessException,
            UnsupportedEncodingException {
        if (patientId != null) {
            Patient patient = patientDao.get(patientId);
            // 患者不存在
            if (patient == null) {
                LOGGER.error("getDoctors Error ; patient not exist : patientId {}, keyword {}, departmentId {}, cityId {}", patientId, keyword,
                             departmentId, cityId);
                throw new PatientBusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
            }
            // 患者账号被禁用
            if (patient.getAccountStatus() != 0) {
                LOGGER.error("getDoctors Error ; patient account status wrong : patientId {}, keyword {}, departmentId {}, cityId {}", patientId,
                             keyword, departmentId, cityId);
                throw new PatientBusinessException(ExceptionCodes.PATIENT_ACCOUNT_ERROR);
            }
        }
        PageVO<DoctorVO> pageVO = new PageVO<>();
        // 配置文件获取搜索服务地址
        String searchUrl = ConfigKeyUtil.getString(ConfigKeyUtil.DOCTOR_SEARCH_URL);
        // 参数处理
        // 拼接关键字
        if (StringUtils.isNotBlank(keyword)) {
            searchUrl += "keyword=" + URLEncoder.encode(keyword, "utf-8") + "&";
        } else {
            searchUrl += "keyword=&";
        }
        // 拼接科室
        if (departmentId != null) {
            searchUrl += "departmentId=" + departmentId + "&";
        } else {
            searchUrl += "departmentId=0&";
        }
        // 拼接城市id
        if (cityId != null) {
            searchUrl += "parentCityId=" + cityId + "&";
        } else {
            searchUrl += "parentCityId=0&";
        }

        searchUrl += "page=" + page + "&num=" + num;
        // 远程调用
        //String result = HttpRequestUtils.doGet(searchUrl);
        PageVO<DoctorSearchVO> pageObject = RestHttpUtil.get()
                                                        .url(searchUrl)
                                                        .executeData(new ParameterizedTypeReference<ResponseVO<PageVO<DoctorSearchVO>>>() {
                                                        });
        try {

            // 分页信息转换
            // 结果集处理
            List<DoctorSearchVO> pageResult = pageObject.getResult();
            List<DoctorVO> doctorVos = new ArrayList<>();
            for (DoctorSearchVO dsv : pageResult) {
                // 医生信息VO
                DoctorVO doctorVo = new DoctorVO();
                doctorVo.setDoctorId(dsv.getId());
                // 科室
                doctorVo.setDepartment(dsv.getDepartmentName());
                // 职称
                doctorVo.setTitle(dsv.getTitle());
                // 医院
                doctorVo.setHospital(dsv.getHospitalName());
                // 好评
                doctorVo.setPraiseNum(dsv.getPraiseCount());
                // 名称
                doctorVo.setName(dsv.getName());
                // 头像地址 ( 按条件查询医生，必为已认证医生，头像正常处理 )
                // TODO 搜索服务是否默认查询已认证医生
                doctorVo.setPhoto(ImgUtil.getDoctorPhoto(dsv.getHeadUrl()));
                // 专长
                doctorVo.setExpertise(dsv.getExpertise());
                // 与当前用户关系 TODO:优化，缓存医患关系
                boolean personal = patientDao.existPersonalRelation(patientId, dsv.getId());
                boolean attend = patientDao.existAttendRelation(patientId, dsv.getId());
                if (personal) {
                    doctorVo.setRelation(PatientConstant.RELATIONN_TYPE_PERSONAL);
                } else if (attend) {
                    doctorVo.setRelation(PatientConstant.RELATIONN_TYPE_ATTEND);
                } else {
                    doctorVo.setRelation(PatientConstant.RELATIONN_TYPE_NULL);
                }
                // 在线状态，默认都为在线状态 V1.4 @2015-11-16 by zhoupeipei
                doctorVo.setStatus(1);
                // if (validateDoctorOnline(dsv.getId())) {
                // doctorVo.setStatus(1);
                // } else {
                // doctorVo.setStatus(0);
                // }

                // 加入结果页集合
                doctorVos.add(doctorVo);
            }
            pageVO = CustomizedPageConvertor.buildPageVOWithCustom(pageObject.getPageNo(), pageObject.getPageSize(), doctorVos,
                                                                   pageObject.getTotalCount(), pageObject.getOrder());
        } catch (Exception e) {
            LOGGER.error("search doctor error ! keyword :{},departmentId :{},cityId :{},patientId :{},page :{},num :{}", keyword, departmentId,
                         cityId, patientId, page, num, e);
        }
        // 处理返回
        return pageVO;
    }

    @Override
    public DoctorVO getNewPersonalDoctor(Long patientId) {
        Doctor doctor = doctorDao.getNewPersonalDoctor(patientId);
        if (doctor == null) {
            // 若患者目前未扫码关注过医生
            DoctorVO emptyVO = new DoctorVO();
            emptyVO.setHospital("");
            emptyVO.setHospitalId(0);
            emptyVO.setDepartment("");
            emptyVO.setDepartmentId(0);
            emptyVO.setName("");
            emptyVO.setDoctorId(0L);
            emptyVO.setBindTime(0L);

            return emptyVO;
        }
        DoctorVO newPersonal = buildNewPersonalDoctor(doctor);
        // 患者关注医生时间（患者微信端：创建病例默认时间 使用 ）
        Date bindDate = new Date();
        if (patientDao.existPersonalRelation(patientId, doctor.getId())) {
            bindDate = patientPersonDoctorDao.findByPatientIdAndDoctorId(patientId, doctor.getId()).getCreateAt();
        }
        newPersonal.setBindTime(bindDate.getTime());
        return newPersonal;
    }

    /**
     * 构建最新私人医生信息 PatientDoctorService.buildNewPersonalDoctor()<BR> <P>Author :
     * Amber </P> <P>Date : 2015年9月11日 </P>
     *
     * @param doctor
     *
     * @return
     */
    private DoctorVO buildNewPersonalDoctor(Doctor doctor) {
        DoctorVO newPersonal = new DoctorVO();
        newPersonal.setDoctorId(doctor.getId());
        newPersonal.setName(doctor.getName());
        // 缓存获取医生科室信息
        // 未认证医生无科室等数据处理,医生手动输入科室departmentId为0
        if (null != doctor.getDepartmentId() && 0 != doctor.getDepartmentId()) {// 存在科室，且非自定义
            DepartmentVO departmentVO = BaseParamCacheUtil.getDepartmentFromRedis(doctor.getDepartmentId());
            newPersonal.setDepartmentId(doctor.getDepartmentId());
            if (departmentVO != null) {// 获取科室名
                newPersonal.setDepartment(departmentVO.getName());
            } else {// 缓存没有科室信息，数据库读取
                Department department = departmentRepository.get(doctor.getDepartmentId());
                newPersonal.setDepartment(department.getName());
            }
        } else {
            newPersonal.setDepartmentId(0);
            newPersonal.setDepartment(doctor.getCustomDepartment() == null ? "" : doctor.getCustomDepartment());
        }
        // 获取医生医院信息
        if (null != doctor.getHospitalId()) {
            String hpName = doctorDao.getHospitalName(doctor.getHospitalId());
            newPersonal.setHospitalId(doctor.getHospitalId());
            if (StringUtils.isEmpty(hpName)) {
                newPersonal.setHospital(doctor.getHospitalName());
            } else {
                newPersonal.setHospital(hpName);
                newPersonal.setHospital(hpName);
            }
        } else {
            newPersonal.setHospitalId(0);
            newPersonal.setHospital("");
        }
        return newPersonal;
    }

    @Override
    public boolean getPersonalRelation(Long patientId, Long doctorId) {
        return patientDao.existPersonalRelation(patientId, doctorId);
    }

    private PersonalRelation findPersonalDoctor(Doctor doctor, Patient patient) {
        // 处理返回，医生信息
        DoctorVO doctorVo = new DoctorVO();
        doctorVo.setDoctorId(doctor.getId());
        doctorVo.setName(doctor.getName() == null ? "" : doctor.getName());
        doctorVo.setPhotoOnState(doctor.getHeadUrl(), doctor.getStatus());
        doctorVo.setAuthStatus(doctor.getStatus());
        // 图文咨询收费设置
        DoctorCharge doctorCharge = doctorChargeService.getDoctorChargeInfo(doctor.getId(), DrChargeConfig.TYPE_MESSAGE);
        // 如果开启图文咨询
        if (doctorCharge != null) {
            doctorVo.setConsultCost(doctorCharge.getCharge());
        }

        // 处理返回，患者信息
        PatientVO patientVo = new PatientVO();
        patientVo.setPatientId(patient.getId());
        // 患者完善过资料时返回真是姓名
        patientVo.setNickName(StringUtils.isNotEmpty(patient.getName()) ? patient.getName()
                                      : (StringUtils.isEmpty(patient.getNickName()) ? "互联网医院患者" : patient.getNickName()));
        patientVo.setCanTalk(patient.cantalk());

        // 处理返回，关联信息
        PersonalRelation personalRelation = new PersonalRelation();
        personalRelation.setDoctor(doctorVo);
        personalRelation.setPatient(patientVo);

        // 本时间与实际关联时间可能有误差，不影响页面显示
        personalRelation.setCreateAt(new Date());
        return personalRelation;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PersonalRelation addDoctorToNewPatient(Patient patient, Long doctorId) throws Exception {
        // 患者验空
        if (patient == null) {
            LOGGER.error("new patient bind doctor,patient not exist!");
            throw new BusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
        }
        // 医生账户
        Doctor doctor = doctorDao.get(doctorId);
        // 绑定
        PersonalRelation personalRelation = addPersonalDoctor(patient, doctor, PatientConstant.PERSONAL_SOURCE_WEIXIN, false);
        // 设置首推
        patient.setRecomDoctor(doctorId);
        return personalRelation;
    }

    /**
     * 根据患者id查询关注医生列表
     *
     * @param patientId
     * @param page
     * @param num
     *
     * @return
     *
     * @throws Exception
     * @Author linhf
     * @Date 2016年4月21日
     * @see com.naiterui.ehp.bs.patient.service.IPatientDoctorService#getPrivateDoctorList(java.lang.Long,
     * int, int)
     * @since 1.0.0
     */
    @Override
    public PageVO<DoctorVO> getPrivateDoctorList(Long patientId, int page, int num) {
        // 根据患者id查询患者的所有私人医生列表ids
        Integer accountStatus = 0;// 账户状态0:正常，1:禁用
        PageVO<DoctorVO> doctorList = getMyPrivateDoctor(patientId, page, num, accountStatus);
        return doctorList;
    }

    /**
     * 获取患者与某医生最近的一条聊天信息 PatientDoctorService.getFirstMesageByPD()
     *
     * @param patientId
     * @param doctorId
     *
     * @return
     *
     * @Author Amber
     * @Date 2015年12月3日
     * @since 1.0.0
     */
    private MessageVO getFirstMesageByPD(Long patientId, Long doctorId) {
        if (patientId == null || doctorId == null) {
            return null;
        }
        List<Message> messages = messageDao.queryFirstMessages(patientId, doctorId);
        if (messages.size() > 0) {
            Message message = messages.get(0);
            MessageVO messageVO = new MessageVO();
            messageVO.setType(message.getType());
            messageVO.setTime(message.getSendTime());
            if (message.getType() == PatientConstant.MESSAGE_TYPE_TEXT) {
                // 文本消息或超链接设置消息内容
                messageVO.setContent(message.getContent().toString());
            } else if (message.getType() == PatientConstant.MESSAGE_TYPE_HYPERLINK) {
                Object content = message.getContent();
                if (content instanceof LinkedHashMap) {
                    messageVO.setContent(((LinkedHashMap) content).get("orgin").toString());
                }
            } else {
                messageVO.setContent(JsonMapper.toJson(message.getContent()));
            }
            // 查询消息对应的会话
            ConsultSession consultSession = consultSessionRepository.findFirstByPatientIdAndDoctorIdOrderByStartTimeDesc(patientId, doctorId);
            if (consultSession != null) {
                // 返回会话id
                messageVO.setConsultSessionId(consultSession.getSessionId());
                // 返回会话开始时间
                messageVO.setStartTime(consultSession.getStartTime().getTime());
                // 返回会话结束时间
                if (consultSession.getEndTime() != null) {
                    messageVO.setEndTime(consultSession.getEndTime().getTime());
                }

            }
            return messageVO;
        }
        return null;
    }

    @Override
    public DoctorVO getDoctorByDoctorId(Long doctorId) {
        if (doctorId == null) {
            return null;
        }

        Doctor doctor = doctorDao.get(doctorId);
        DoctorVO doctorVo = new DoctorVO();
        // 医生姓名
        doctorVo.setName(doctor.getName());
        doctorVo.setAuthStatus(doctor.getStatus());
        // 认证过的医生显示
        doctorVo.setPhoto(doctor.getHeadUrl());
        doctorVo.setTitle(doctor.getTitleId() == null ? "" : TitleUtil.getTitle(doctor.getTitleId()));
        // 医院名称
        if ((doctor.getHospitalId() != null) && (0 != doctor.getHospitalId())) {
            doctorVo.setHospital(doctorDao.getHospitalName(doctor.getHospitalId()));
        } else {
            // 医院名称
            doctorVo.setHospital(doctor.getHospitalName());
        }
        // 医生所在科室
        if (doctor.getDepartmentId() != null && doctor.getDepartmentId() != 0) {// 存在科室，且非自定义
            doctorVo.setDepartmentId(doctor.getDepartmentId());
            // 医生有科室，先从缓存查询科室信息
            DepartmentVO departmentVO = BaseParamCacheUtil.getDepartmentFromRedis(doctor.getDepartmentId());
            if (departmentVO != null) {
                // 设置患者科室
                doctorVo.setDepartment(departmentVO.getName());
            } else {
                // 缓存中没有则去读库查询
                Department department = departmentRepository.get(doctor.getDepartmentId());
                if (department != null) {
                    // 设置患者科室
                    doctorVo.setDepartment(department.getName());
                }
            }
        } else {
            doctorVo.setDepartment(doctor.getCustomDepartment() == null ? "" : doctor.getCustomDepartment());
        }

        return doctorVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long getFirstDoctorByPatient(Long patientId) {
        // 按照创建时间升序查找私人医生
        List<PatientPersonDoctor> ppdList = patientPersonDoctorDao.getPersonPatientBindDoctor(patientId);
        Long doctorId = null;
        if (ppdList != null && ppdList.size() > 0) {
            for (PatientPersonDoctor ppd : ppdList) {
                Doctor doctor = doctorDao.get(ppd.getPersonPK().getDoctorId());
                // 医生状态为正常
                if ((doctor != null) && (doctor.getAccountStatus() == 0)) {
                    doctorId = doctor.getId();
                    break;
                }
            }
        }
        return doctorId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveCheckedDoctor(Long patientId, Long doctorId) throws PatientBusinessException {
        // 校验医患关系-正常
        List<PatientPersonDoctor> patientPersonDoctorList = patientPersonDoctorDao
                .getPatientDoctorRelation(patientId, doctorId, PatientPersonDoctor.RELATION_FLAG_NORMALITY);
        //
        if (CollectionUtils.isEmpty(patientPersonDoctorList)) {
            LOGGER.error("记录需求清单患者选中的医生 ; 医患关系不存在 : patientId {}, doctorId {}", patientId, doctorId);
            throw new PatientBusinessException(ExceptionCodes.PATIENT_DOCTOR_RELATION_ERROR);
        }

        // 保存选择的医生id到redis
        RedisUtil.hashOps().hset(PatientConstant.REDIS_KEY_PATIENT_DRUG_CHECKED_DOCTOR_ID, patientId.toString(), doctorId.toString());
    }

    @Override
    public DoctorCheckVO getRecomAndPersonalDoctorInfo(Long patientId) throws PatientBusinessException {
        Patient patient = patientDao.get(patientId);
        PatientValidation.accountStatusValidate(patient, String.format("getMyDoctors Error ; patient not exist : patientId %s", patientId));
        DoctorCheckVO doctorCheckVO = new DoctorCheckVO();
        // 判断上级医生
        Long recomDoctor = patient.getRecomDoctor();
        if (recomDoctor == null) {
            doctorCheckVO.setHasRecom(false);
        } else {
            doctorCheckVO.setHasRecom(true);
        }

        // 判断私人医生
        List<PatientPersonDoctor> ppdList = patientPersonDoctorDao.getPersonPatientBindDoctor(patientId);
        if (CollectionUtils.isEmpty(ppdList)) {
            doctorCheckVO.setHasPersonal(false);
        } else {
            // 如果所有的私人医生均已屏蔽，则无私人医生
            Set<Long> deleteDoctorIds = new HashSet<>();
            for (PatientPersonDoctor patientPersonDoctor : ppdList) {
                if (patientPersonDoctor.getRelationFlag() != null
                        && patientPersonDoctor.getRelationFlag().intValue() == PatientConstant.PERSONAL_DOCTOR_RELATION_FLAG_DELETE) {
                    deleteDoctorIds.add(patientPersonDoctor.getPersonPK().getDoctorId());
                }
            }
            if (deleteDoctorIds.size() == ppdList.size()) {
                doctorCheckVO.setHasPersonal(false);
            } else {
                doctorCheckVO.setHasPersonal(true);
            }
        }
        return doctorCheckVO;
    }

    @Override
    public RevisitDoctorBO getRevisitDoctor(Long patientId, Long doctorId) {
        DoctorVO doctorVO = getDoctorByDoctorId(doctorId);
        Integer messageCharge = drMessageConsultChargeDao.getChargeById(doctorId, patientId);
        return RevisitDoctorBO
                .builder()
                .consultPrice(messageCharge)
                .department(doctorVO.getDepartment())
                .doctorName(doctorVO.getName())
                .headUrl(doctorVO.getPhoto())
                .hospital(doctorVO.getHospital())
                .status(doctorVO.getAuthStatus())
                .title(doctorVO.getTitle())
                .build();
    }

    @Override
    public Integer startChat(Long patientId, Long doctorId) {
        boolean existsKey = RedisUtil.keyOps().existsKey(CommonConstant.PATIENT_PERSONAL_DOCTOR_FIRST_BIND + doctorId + "_" + patientId);
        // 如果赠送的次数存在就直接告诉可以开会话
        if(existsKey){
            return CacheConstant.MESSAGE_HANDLE_NORMAL;
        }
        Patient patient = patientDao.get(patientId);
        Doctor doctor = doctorDao.get(doctorId);
        return startChat(patient, doctor, true);
    }

    @Override
    public Integer startChat(Patient patient, Doctor doctor, boolean needAuth) {
        if (doctor == null || patient == null) {
            LOGGER.error("startChat ERROR ; doctor or patient info not exist : patient={},doctor={},needAuth={}", patient, doctor, needAuth);
            return CacheConstant.MESSAGE_HANDLE_NORMAL;
        }
        Long patientId = patient.getId();
        Long doctorId = doctor.getId();

        RedisUtil.hashOps().hset(CommonConstant.CONSULT_PATIENT_AIM, patientId.toString(), doctorId.toString());
        RedisUtil.hashOps().hset(CommonConstant.CONSULT_PATIENT_AIM_NAME, patientId.toString(), doctor.getName());

        if (!needAuth) {
            //若无需认证等客服消息提示，忽略校验及相关推送
            LOGGER.info("startChat INFO ; do not needAuth : patient={},doctor={}", patient, doctor);
            return CacheConstant.MESSAGE_HANDLE_NORMAL;
        }

        /*if (Patient.REAL_STATUS_SUCCESS != patient.getStatus()) {
            //患者未认证：客服消息——>您还未完善个人信息，点击完善(个人信息认证链接)
            customMsgApiService.sendSystemMsg(patient.getOpenId(), "感谢您信任并使用" + ConfigUtil.getString("ehp.hospital.name") + "，线上问诊需实名认证，否则无法咨询医生，感谢您的配合。\n----------\n" +
                    webAuthorizeApiServiceImpl.getWebAuthorizeUrlSilent(ConfigUtil.getString("wechat.redirect.common.url"),
                                                                             PatientJumperEnum.PATIENT_AUTH.encodeWxParamStr(), "点击完成认证"));
            return CacheConstant.MESSAGE_HANDLE_NORMAL;
        }*/

        //医患从未产生过病历，需要等待医生首次填写病历
        /*boolean existMedicalCache = RedisUtil.hashOps().hexist(CommonConstant.CONSULT_MEDICAL_RECORD_EXIST, patientId + "_" + doctorId);
        if (!existMedicalCache) {
            //根据卫健委相关规定，需要医生为您补充线下就诊病历后，方可针对相同诊断进行线上复诊，请耐心等待XX医生完善您的病历信息。
            customMsgApiService.sendSystemMsg(patient.getOpenId(), "根据卫健委相关规定，需要医生为您补充线下就诊病历后，才能针对相同诊断进行线上复诊，请耐心等待" + doctor.getName() +
            "医生完善您的病历信息。");
            return;
        }*/

        //免责协议是否已同意（会话维度）
        if (!RedisUtil.keyOps().existsKey(CommonConstant.CONSULT_DISCLAIMER_SWITCH) && !RedisUtil.hashOps()
                                                                                                .hexist(CommonConstant.CONSULT_DISCLAIMER_ACTIVE,
                                                                                                        patientId + "_" + doctorId)) {
           /* customMsgApiService.sendSystemMsg(patient.getOpenId(),
                    String.format(ConfigKeyUtil.getString("wachat.sysmsg.disclaimer"), doctor.getName(),
                            webAuthorizeApiServiceImpl.getWebAuthorizeUrlSilent(ConfigUtil.getString("wechat.redirect.common.url"),
                                    PatientJumperEnum.PATIENT_MEDICAL_RECORD_LIST.encodeWxParamStr(doctorId.toString()), "点击选择病历或填写病情描述")));*/
            // 2019/11/27 5:05 下午 高亮亮 省去客服消息跳转，客户端直接跳转页面
            return CacheConstant.MESSAGE_HANDLE_REDIRECT;
        }

        //正常可咨询，又进行中，关闭页面进入聊天框，否则跳转
        if (RedisUtil.keyOps().existsKey(CacheConstant.getSessionKey(doctorId, patientId))) {
            /*customMsgApiService.sendSystemMsg(patient.getOpenId(),
                "您已选择向" + doctor.getName() + "医生咨询，点击左下角小键盘图标，在聊天框内描述您的不适症状或语音提问，即可享受在线问诊服务了~");*/
            return CacheConstant.MESSAGE_HANDLE_NORMAL;
        }

        return CacheConstant.MESSAGE_HANDLE_REDIRECT;
    }

    @Override
    public Integer agreeDisclaimer(Long patientId, Long inquirerId, Long doctorId, Integer type, String recordId, Boolean agree, Integer consultType)
        throws BusinessException {
        // 就诊人需要实名之后才能发起问诊
        PatientInquirerInfo patientInquirerInfo = patientInquirerRepository.get(inquirerId);
        if (patientInquirerInfo.getAccountStatus().equals(PatientInquirerInfo.ACCOUNT_STATUS_NO)) {
            // 就诊人信息不完善，请补充实名信息
            throw new BusinessException(ExceptionCodes.PATIENT_INQUIRER_NOT_AUTH_CONSULT);
        }

        Patient patient = patientDao.get(patientId);
        Doctor doctor = doctorDao.get(doctorId);
        // 医生账号禁用
        if (doctor.getAccountStatus() != 0) {
            LOGGER.error("agreeDisclaimer Error ; doctor account status wrong : patientId {}, doctorId {}", patientId, doctorId);
            throw new PatientBusinessException(ExceptionCodes.DOCTOR_ACCOUNT_DISABLE);
        }

        Integer clinicFirst = 0;
        // 查询医患无关联关系，并且医生是专家名医，则直接建立绑定关系，并且clinicFirst置成1。
        PatientPersonDoctor patientPersonDoctor = patientPersonDoctorRepository.findFirstByPersonPKPatientIdAndPersonPKDoctorId(patientId,
                                                                                                                                     doctorId);
        UserLabel userLabel = userLabelRepository.findFirstByUserTypeAndLabelIdAndUserId(UserLabel.USER_TYPE_DOCTOR, Label.CLINIC_LABEL_ID,
                                                                                              doctorId);
        if (null == patientPersonDoctor && null != userLabel) {
            try {
                LOGGER.info("首次咨询专家名医，建立关联关系，doctorId {}, patientId {}", doctorId, patientId);
                // 建立医患绑定关系
                addPersonalDoctor(doctorId, patientId, 0, PatientConstant.PERSONAL_SOURCE_WEIXIN);
                clinicFirst = 1;
            } catch (Exception e) {
                LOGGER.error("专家名医首次建立关联关系异常，doctorId {}, patientId {}", doctorId, patientId, e);
                throw new BusinessException(ExceptionCodes.FAILED);
            }
        }

        if (!agree) {
            return CacheConstant.MESSAGE_HANDLE_NORMAL;
        }

        if (patient == null || doctor == null) {
            LOGGER.error("agreeDisclaimer ERROR ; doctor or patient info not exist : patientId={},doctorId={},type={},recordId={},agree={}",
                         patientId, doctorId, type, recordId, agree);
            return CacheConstant.MESSAGE_HANDLE_NORMAL;
        }

        String sessionKey = CacheConstant.getSessionKey(doctorId, patientId);
        if (consultType == ConsultType.VIDEO) {
            sessionKey = CacheConstant.getVideoSessionKey(doctorId, patientId);
        }
        Integer chargeCost = drMessageConsultChargeDao.getChargeById(doctorId, patientId, consultType);
        if (chargeCost != null && chargeCost > 0 && !RedisUtil.keyOps().existsKey(sessionKey)) {
            RedisUtil.hashOps().hset(
                CacheConstant.getSessionPrepareKey(), sessionKey, CacheConstant.getSessionPrepareValue(type, recordId));
            return CacheConstant.MESSAGE_HANDLE_REDIRECT;
        }
        // 只有图文问诊进行消息发送
        if (consultType == ConsultType.VIDEO) {
            RedisUtil.hashOps().hset(
                    CacheConstant.getSessionPrepareKey(), CacheConstant.getVideoSessionKey(doctorId, patientId), CacheConstant.getSessionPrepareValue(type, recordId));
        } else {
            messageSendService.sendRecord(patientId, inquirerId, doctorId, type, recordId, patient.getOpenId(), doctor.getName(), clinicFirst);
        }
        return CacheConstant.MESSAGE_HANDLE_NORMAL;
    }

    @Override
    public Integer startConsult(ByConsultParamVO paramVO) throws BusinessException {
        PatientDiseaseBO diseaseBO = new PatientDiseaseBO();
        BeanUtil.copyProperties(paramVO, diseaseBO);
        String diseaseId = emrFeginClient.addPatientDisease(paramVO.getPatientId(), diseaseBO);
        Integer result = agreeDisclaimer(paramVO.getPatientId(), paramVO.getInquirerId(), paramVO.getDoctorId(), paramVO.getType(), diseaseId, true,
            paramVO.getConsultType());
        return result;
    }



    @Override
    public DoctorVO findDoctorDetail(Long doctorId, Long patientId) throws BusinessException {
        // 密钥验证
        Doctor doctor = doctorDao.get(doctorId);
        // 医生不存在
        if (doctor == null) {
            LOGGER.error("getDoctorDetail Error ; doctor not exist : patientId {}, doctorId {}", patientId, doctorId);
            throw new PatientBusinessException(ExceptionCodes.DOCTOR_NOT_EXIST);
        }

        // 医生账号异常
//        if (doctor.getAccountStatus() != 0) {
//            LOGGER.error("getDoctorDetail Error ; doctor account status wrong : patientId {}, doctorId {}", patientId, doctorId);
//            throw new PatientBusinessException(ExceptionCodes.DOCTOR_ACCOUNT_DISABLE);
//        }

        // 看全部医生是否禁用

        DoctorVO doctorVo = new DoctorVO();

        if (patientId != null) {
            Patient patient = patientDao.get(patientId);
            // 患者不存在
            if (patient == null) {
                LOGGER.error("getDoctorDetail Error ; patient not exist : patientId {}, doctorId {}", patientId, doctorId);
                throw new PatientBusinessException(ExceptionCodes.PATIENT_NOT_EXIST);
            }
            // 患者账号被禁用
            if (patient.getAccountStatus() != 0) {
                LOGGER.error("getDoctorDetail Error ; patient account status wrong : patientId {}, doctorId {}", patientId, doctorId);
                throw new PatientBusinessException(ExceptionCodes.PATIENT_ACCOUNT_ERROR);
            }
        }
        // 是否是首次绑定
        boolean firstBind = RedisUtil.keyOps().existsKey(CacheConstant.getFirstBindKey(doctorId, patientId));
        doctorVo.setFirstBinding(firstBind);
        // 是否可以发送消息
        boolean sessionKey = RedisUtil.keyOps().existsKey(doctorId + "_" + patientId);
        if (firstBind || sessionKey) {
            doctorVo.setSendMessage(true);
        } else {
            doctorVo.setSendMessage(false);
        }

        // 获取患者未支付账单数 新版本不在使用，屏蔽by xuchong
        // int unPayNum = consultBillService.getPatientUnPayBillNum(patientId);

        // 基础信息
        doctorVo.setDoctorId(doctor.getId());
        doctorVo.setName(doctor.getName() == null ? "" : doctor.getName());
        doctorVo.setAuthStatus(doctor.getStatus());
        doctorVo.setPhotoOnState(doctor.getHeadUrl(), doctor.getStatus());
        doctorVo.setTitle(doctor.getTitleId() == null ? "" : TitleUtil.getTitle(doctor.getTitleId()));
        doctorVo.setExpertise(doctor.getExpertise() == null ? "" : doctor.getExpertise());
        doctorVo.setIntroduction(doctor.getIntroduction() == null ? "" : doctor.getIntroduction());
        doctorVo.setAccountStatus(doctor.getAccountStatus());
        // 获取医生患者数
        Integer paitentNum = doctorDao.getPatientNum(doctorId);
        doctorVo.setPatientNum(paitentNum);

        //医生服务次数（会话）
        Integer videoConsultCount = Optional.ofNullable(videoConsultFeignClient.getVideoConsultCountByStatus(doctorId, DrVideoConsult.VIDEO_CONSULT_STATUS_ALL)).orElse(0);
        long textConsultCount = consultSessionRepository.countByDoctorIdAndEndTimeIsNotNull(doctorId);
        Integer allCount = videoConsultCount + (int) textConsultCount;
        doctorVo.setServiceNum(String.valueOf(allCount));
        //获取好评率
        Map<Long, String> praiseRateMap = consultCommentService.getPraiseRate(Lists.newArrayList(doctorId));
        doctorVo.setPraiseRate(praiseRateMap.get(doctorId));

        // 是否已关注 绑定也算关注
        boolean attended = patientDao.existAttendRelation(patientId, doctorId);
        boolean personal = patientDao.existPersonalRelation(patientId, doctorId);
        if (personal) {
            doctorVo.setRelation(PatientConstant.RELATIONN_TYPE_PERSONAL);
        } else if (attended) {
            doctorVo.setRelation(PatientConstant.RELATIONN_TYPE_ATTEND);
        } else {
            doctorVo.setRelation(PatientConstant.RELATIONN_TYPE_NULL);
        }
        // 科室
        if (doctor.getDepartmentId() != null && doctor.getDepartmentId() != 0) {// 存在科室，且非自定义
            // 医生有科室，先从缓存查询科室信息
            DepartmentVO departmentVO = BaseParamCacheUtil.getDepartmentFromRedis(doctor.getDepartmentId());
            if (departmentVO != null) {
                // 设置患者科室
                doctorVo.setDepartment(departmentVO.getName());
            } else {
                // 缓存中没有则去读库查询
                Department department = departmentRepository.get(doctor.getDepartmentId());
                if (department != null) {
                    // 设置患者科室
                    doctorVo.setDepartment(department.getName());
                }
            }
        } else {
            doctorVo.setDepartment(doctor.getCustomDepartment() == null ? "" : doctor.getCustomDepartment());
        }

        // 医院名称
        String hpName = doctorDao.getHospitalName(doctor.getHospitalId());
        if (StringUtils.isEmpty(hpName)) {
            doctorVo.setHospital(doctor.getHospitalName() == null ? "" : doctor.getHospitalName());
        } else {
            doctorVo.setHospital(hpName);
        }

        // 在线状态 默认都为在线状态 V1.4 @2015-11-16 by zhoupeipei
        doctorVo.setStatus(1);

        // 线上线下问诊时间
        doctorVo.setOutlineTime(getDoctorConsults(doctorId));
        // 互联网医院时间
        // doctorVo.setOnlineTimes(getDoctorBayConsults(doctorId));

        // 门诊地址
        if (doctor.getHospitalId() != null) {
            Hospital hospital = hospitalRepository.get(doctor.getHospitalId());
            doctorVo.setOutlineOn(hospital == null || hospital.getAddress() == null ? "" : hospital.getAddress());
        } else {
            doctorVo.setOutlineOn("");
        }
        // 图文咨询支付相关，已经不需要，屏蔽by xuchong
        // setDoctorChargeInfo(doctorVo);
        // 设置当前患者未支付账单数，已经不需要，屏蔽by xuchong
        // doctorVo.setNoPayNum(unPayNum);

        Integer consultCost = drMessageConsultChargeDao.getChargeById(doctorId, patientId);
        // 设置是否收费
        doctorVo.setConsultPayType(consultCost > 0 ? CommonConstant.CONSULT_PAYTYPE_CHARGE : CommonConstant.CONSULT_PAYTYPE_FREE);
        // 图文咨询费用
        doctorVo.setConsultCost(consultCost);

        // 查询医生和患者之间是否有进行中的会话
        Map<Long, Boolean> aliveMap = consultSessionRemoter.aliveMap(patientId, doctorId);
        boolean alive = aliveMap.get(doctorId) == null ? false : aliveMap.get(doctorId);
        // 有开启会话订单已付费，没有都设置为未付费
        doctorVo.setPayStatus(alive ? DrMessageConsultOrder.MESSAGE_PAY_STATUS_YES : DrMessageConsultOrder.MESSAGE_PAY_STATUS_NO);

        // 获取医生开通服务
//        DrServiceItems drServiceItem = drServiceItemService.getDoctorChargeItems(doctor.getId());
        // 视频咨询获取
        DoctorCharge doctorCharge = doctorChargeDao.getCharge(doctor.getId(), DrChargeConfig.TYPE_VIDEO);
        if (doctorCharge != null) {

            Integer videoCost = (doctorCharge.getCharge() != null) ? doctorCharge.getCharge() : Integer.valueOf(0);
            Long chargePeriod = (doctorCharge.getChargePeriod() != null) ? doctorCharge.getChargePeriod() : Long.valueOf(0);
            // 转化成分钟
            Integer videoLength = chargePeriod.intValue() / (1000 * 60);
            // 设置视频咨询费用
            doctorVo.setVideoCost(videoCost);
            // 设置视频咨询收费时长
            doctorVo.setVideoLength(videoLength);
            // 已开通视频咨询
            doctorVo.setIsOpenVideo(1);
        } else {
            doctorVo.setVideoLength(90000 / (1000 * 60));
            doctorVo.setVideoCost(0);
            // 未开通视频咨询
            doctorVo.setIsOpenVideo(0);
        }

        //获取最新5条评价
        PageParamVO pageParamVO = new PageParamVO(1, 5);
        doctorVo.setConsultCommnetPage(consultCommentService.findAllByDoctorId(doctorId, pageParamVO));

        // 微信端返回打赏列表，app端不返回打赏列表
        /*
        if (clientType == 2) {
            // 设置医生打赏信息
            PageVO<RewardVO> pageVoResult = this.rewardService.getRewardListByDoctorId(doctorId, 1, 2);
            doctorVo.setTotleReward(pageVoResult.getTotalCount());
            // 设置打赏列表
            doctorVo.setRewardList(pageVoResult.getResult());
        }
        */
        ConsultSession consultSession = consultSessionRepository.findFirstByPatientIdAndDoctorIdAndEndTimeIsNull(patientId, doctorId);
        doctorVo.setActualCost(null != consultSession ? consultSession.getCost() : null);
        return doctorVo;
    }

    /**
     * 场景值处理
     * @param patientId
     * @param scene
     */
    private void sceneHandle(Long patientId, String scene) throws BusinessException {
        if(StringUtils.isBlank(scene)){
            return;
        }
        String[] sceneArr = scene.split("_");
        if(sceneArr.length < 2){
            return;
        }
        String event = sceneArr[0];
        String param = sceneArr[1];
        PaQrCodeEnum eventEnum = PaQrCodeEnum.getByValue(event + "_");
        switch (eventEnum) {
            // 邀请患者
            case INVITE:
                // 绑定医患关系
                Long doctorId = Long.parseLong(param);
                this.addPersonalDoctor(doctorId, patientId, 4, true);
                break;
            default:
        }
    }
}
